package robbie.imageprocessing;

import java.awt.color.ColorSpace;

/**
 * @author Robbie
 */
public class CIELabColorSpace extends ColorSpace {

    private static final long serialVersionUID = 1L;

    private final float[][] mMatrix = {{0.412453f, 0.357580f, 0.180423f},
            {0.212671f, 0.715160f, 0.072169f},
            {0.019334f, 0.119193f, 0.950227f}};

    private final float[][] mReverseMatrix = {
            {3.240479f, -1.537150f, -0.498535f},
            {-0.969256f, 1.875992f, 0.041556f},
            {0.055648f, -0.204043f, 1.057311f}};

    public CIELabColorSpace() {
        super(ColorSpace.TYPE_Lab, 3);
    }

    @Override
    public float[] fromRGB(float[] rgbvalue) {
        float[] cieLab = fromRGBtoXYZ(rgbvalue);

        cieLab[0] = cieLab[0] / 0.950456f;
        cieLab[2] = cieLab[2] / 1.088754f;

        for (int i = 0; i < 3; i++) {
            if (cieLab[i] > 0.008856f) {
                cieLab[i] = (float) Math.pow(cieLab[i], 0.33334f);
            } else {
                cieLab[i] = 7.787f * cieLab[i] + 0.137931f;
            }
        }

        return new float[]{116 * cieLab[1] - 16,
                500 * (cieLab[0] - cieLab[1]), 200 * (cieLab[1] - cieLab[2])};
    }

    @Override
    public float[] toRGB(float[] cieLab) {
        float y1 = (cieLab[0] + 16) / 116;
        float[] rgbvalue = {(cieLab[1] / 500f) + y1, y1,
                y1 - (cieLab[2] / 200f)};

        for (int i = 0; i < 3; i++) {
            if (rgbvalue[i] > 0.206893f) {
                rgbvalue[i] = (float) Math.pow(rgbvalue[i], 3);
            } else {
                rgbvalue[i] = (rgbvalue[i] - 0.137931f) / 7.787f;
            }
        }

        rgbvalue[0] = rgbvalue[0] * 0.950456f;
        rgbvalue[2] = rgbvalue[2] * 1.088754f;

        return fromXYZtoRGB(rgbvalue);
    }

    @Override
    public float[] fromCIEXYZ(float[] xyzvalue) {
        return fromRGB(fromXYZtoRGB(xyzvalue));
    }

    @Override
    public float[] toCIEXYZ(float[] cieLab) {
        return fromRGBtoXYZ(toRGB(cieLab));
    }

    public float[] fromXYZtoRGB(float[] xyzvalue) {
        float[] colorvalue = new float[3];

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                colorvalue[i] += xyzvalue[j] * mReverseMatrix[i][j];
            }
        }
        for (int i = 0; i < 3; i++) {
            colorvalue[i] = (float) Math.pow(colorvalue[i], 0.4545f) * 255;
        }
        return colorvalue;
    }

    public float[] fromRGBtoXYZ(float[] colorvalue) {
        float[] xyzvalue = new float[3];
        for (int i = 0; i < 3; i++) {
            colorvalue[i] = (float) Math.pow(colorvalue[i] / 255, 2.2);
        }

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                xyzvalue[i] += colorvalue[j] * mMatrix[i][j];
            }
        }
        return xyzvalue;
    }
}
